package cn.autumnorange.app.scheduletask.consumer;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

// 、多线程定时任务
//
//        基于注解设定多线程定时任务
//
//        1、创建多线程定时任务
// @Component注解用于对那些比较中立的类进行注释；
// 相对与在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释
@Slf4j
@Component
@EnableScheduling // 1.开启定时任务
@EnableAsync // 2.开启多线程
public class MultithreadScheduleTask {
  Map<String, Integer> qujanCount = new HashMap<>();
  private int dataSize = 1000;
  private int qujian = 10;
  @Autowired private RedissonClient redissonClient;
  public static final String BUSINESS_EXCHANGE_NAME = "dead-letter-business-exchange";
  @Autowired RabbitTemplate rabbitTemplate;

  public void insertOrder(int[] ctpupk) {};

  //  自旋逐次加区间锁(合同) 生成对应区间数据(订单)
  private void whileLock(int qujian) throws Exception {
    Exception exception = null;
    // 并发处理dataSize=1000-qujian; 有可能 dataSize 有多个方法栈工作变量副本在处理 同时处理-qujian后dataSize的值为最后一个线程的处理值

      dataSize = 1000 - qujian;
      this.qujian += 10;
      log.info("线程:"+Thread.currentThread().getId()+" "+Thread.currentThread().getName()+" qujian: "+qujian+" dataSize: "+dataSize);
    // 再次校验数据库区间是否前移 前移表示已被其他定时任务处理则跳过

    // 开启事务指定期间生成关联合同的订单,生成失败记录日志,发送到订单生成失败队列重试生成订单
    // 部分订单生成失败 合同数据库状态字段标记为合同生成订单失败(-1),单独后续处理,丢到生成失败队列中生成 不影响到下一次查询合同状态定时任务批量生成
    // 交换机名称 路由key路由模式需要 发布订阅模式不需要
    // 合同pk
    int[] ctpupk = new int[10];
    // 生成订单
    insertOrder(ctpupk);
    // 释放事务,释放锁
    rabbitTemplate.convertSendAndReceive(BUSINESS_EXCHANGE_NAME, "", "定时生成订单 " + qujian);
    Thread.sleep(1000 * 4);
  }

  @Async
  @Scheduled(fixedDelay = 1000) // 间隔1秒
  public void first() throws Exception {
    Exception exception = null;
    // 查数据库待生成订单的合同,加上范围区间锁比如区间间隔10 锁key就是10,20,30,... 并发更新时加锁 其他获取不到锁的放弃,取下一区间锁尝试
    // 更新合同已生成订单标志 释放事务,事务需要读已提交防止定时任务读未提交导致区间位置获取异常 释放锁 对id落在区间的订单加区间锁后生成订单 最后推送到消息队列给其他物料系统
    // 消息推送(邮件,短信)系统发送消息队列
    // 锁住区间范围key
    int qujian = this.qujian;
    RLock rLock = redissonClient.getLock(String.valueOf(qujian).intern());
    try {
      if (rLock.tryLockAndStartWatchDog(3000, 10000, TimeUnit.MILLISECONDS)) {
        //        如果当前共享变量已经发生改变也就是表示被其他线程处理完了,则进入下一环节共享变量区间处理
        if (qujian < this.qujian) {
          first();
        }
        // whileLock 共享变量+10变化必须在锁中 不然并发时共享变量还没+10 很多并发线程本地的变量校验是否依然和共享变量一样 一样则又走原来的处理逻辑
        whileLock(qujian);
      }
    } catch (Exception e) {
      exception = e;
    } finally {
      if (rLock.isHeldByCurrentThread() && rLock.isLocked()) {
        rLock.unlock();
      }
      if (exception != null) {
        throw exception;
      }
    }
  }

  @Async
  @Scheduled(fixedDelay = 1000) // 间隔1秒
  public void first2() throws Exception {
    Thread.currentThread().setName("定时任务2");
    first();
  }

  //    }    @Async
  ////    @Scheduled(fixedDelay = 1000) // 间隔1秒
  ////    public void first2() throws Exception {
  ////        whileLock(dataSize);
  ////    }
  //  @Async
  //  @Scheduled(fixedDelay = 2000)
  //  public void second() throws Exception {
  //    Exception exception = null;
  //    RLock rLock = redissonClient.getLock("redislock");
  //    try {
  //      if (rLock.tryLockAndStartWatchDog(3000, 10000, TimeUnit.MILLISECONDS)) {
  //        log.info(
  //            "第二个定时任务开始时间 : "
  //                + LocalDateTime.now().toLocalTime()
  //                + "\r\n线程 : "
  //                + Thread.currentThread().getId()
  //                + " "
  //                + Thread.currentThread().getName()
  //                + " 加锁成功");
  //        Thread.sleep(1000 * 10);
  //
  //      } else {
  //        log.info(
  //            "第二个定时任务开始时间 : "
  //                + LocalDateTime.now().toLocalTime()
  //                + "\r\n线程 : "
  //                + Thread.currentThread().getId()
  //                + " "
  //                + Thread.currentThread().getName()
  //                + " 加锁失败");
  //      }
  //
  //    } catch (Exception e) {
  //      exception = e;
  //    } finally {
  //      if (rLock.isHeldByCurrentThread() && rLock.isLocked()) {
  //        rLock.unlock();
  //        log.info(
  //            "第二个定时任务结束时间 : "
  //                + LocalDateTime.now().toLocalTime()
  //                + "\r\n线程 : "
  //                + Thread.currentThread().getName()
  //                + " 解锁成功");
  //      }
  //      if (exception != null) {
  //        throw exception;
  //      }
  //    }
  //  }
}
